package author

import (
	"HeadLineNews/pkg/db/store"
	"HeadLineNews/pkg/snow"
	"HeadLineNews/pkg/zaplog"
	models "HeadLineNews/proto_models/manage/author_model"
	"HeadLineNews/resp"
	"HeadLineNews/utils/gin_ctx"
	"HeadLineNews/utils/transaction"
	"database/sql"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
)

var (
	errAuthorHasExists = errors.New("该作者已存在")
	errAuthorNotExists = errors.New("该作者不存在")
)

type Repo interface {
	CheckUniqueAuthorName(c *gin.Context, authorName string) (err error)
	InsertAuthor(c *gin.Context, p *models.RequestAddAuthor) (err error)

	CheckAuthorExists(c *gin.Context, authorId string) (authorName string, err error)
	UpdateAuthor(c *gin.Context, p *models.RequestUpdateAuthor) (err error)

	RetrieveAuthor(c *gin.Context) (p *models.ResponseRetrieveAuthor, err error)
	ListAuthor(c *gin.Context, p *models.RequestListAuthor) (data *models.ResponseListAuthor, err error)
	DeleteAuthor(c *gin.Context) (err error)
}

type repo struct {
	db *sqlx.DB
}

func NewRepo() Repo {
	return &repo{db: store.DB()}
}

// CheckUniqueAuthorName 检查重复作者名
func (r *repo) CheckUniqueAuthorName(c *gin.Context, authorName string) (err error) {
	sqlStr := `SELECT COUNT(*) FROM app__author WHERE author_name=?`
	var count int64
	if err = r.db.Get(&count, sqlStr, authorName); err != nil {
		zaplog.L().Error("query author count by author name failed", zaplog.Fields(c, authorName, err)...)
		resp.ServerBusy(c)
		return
	}
	if count > 0 {
		err = errAuthorHasExists
		zaplog.L().Warn("author has exists", zaplog.Fields(c, authorName, nil)...)
		resp.UnprovedParam(c, err.Error())
		return
	}
	return
}

// InsertAuthor 将 author 保存进数据库
func (r *repo) InsertAuthor(c *gin.Context, p *models.RequestAddAuthor) (err error) {
	if err = transaction.Transaction(r.db, func(tx *sqlx.Tx) (te error) {
		p.AuthorDetail.DetailId = snow.GetID()
		sqlStr := `INSERT INTO app__author_detail(
                detail_id, gender, introduce, email, address,
			   create_by, last_update_by, create_time, last_update_time) 
			   VALUES (?,?,?,?,?,?,?,NOW(),NOW())`
		if _, te = tx.Exec(sqlStr,
			p.AuthorDetail.DetailId,
			p.AuthorDetail.Gender,
			p.AuthorDetail.Introduce,
			p.AuthorDetail.Email,
			p.AuthorDetail.Address,
			c.GetString(gin_ctx.UserId),
			c.GetString(gin_ctx.UserId),
		); te != nil {
			zaplog.L().Error("insert author detail failed", zaplog.Fields(c, p.AuthorDetail, te)...)
			return
		}
		sqlStr = `INSERT INTO app__author(author_id, author_name, author_detail) VALUES(?,?,?)`
		if _, te = tx.Exec(sqlStr, snow.GetID(), p.AuthorName, p.AuthorDetail.DetailId); te != nil {
			zaplog.L().Error("insert author failed", zaplog.Fields(c, p, te)...)
			return
		}
		return
	}); err != nil {
		resp.ServerBusy(c)
		return
	}
	return
}

// CheckAuthorExists 根据 id 检查作者是否存在
func (r *repo) CheckAuthorExists(c *gin.Context, authorId string) (authorName string, err error) {
	sqlStr := `SELECT author_name FROM app__author WHERE author_id=?`
	if err = r.db.Get(&authorName, sqlStr, authorId); err != nil {
		if err == sql.ErrNoRows {
			err = errAuthorNotExists
			zaplog.L().Warn("no author was selected by id", zaplog.Fields(c, authorId, nil)...)
			resp.UnprovedParam(c, err.Error())
			return
		}
		zaplog.L().Error("query author by author id failed", zaplog.Fields(c, authorId, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// UpdateAuthor 更新作者
func (r *repo) UpdateAuthor(c *gin.Context, p *models.RequestUpdateAuthor) (err error) {
	query := `UPDATE app__author a, app__author_detail d`
	var args []interface{}
	query += ` SET d.last_update_time=NOW(),d.last_update_by=?`
	args = append(args, c.GetString(gin_ctx.UserId))
	if p.AuthorName != "" {
		query += `,a.author_name=?`
		args = append(args, p.AuthorName)
	}
	if p.AuthorDetail.Gender != "" {
		query += `,d.gender=?`
		args = append(args, p.AuthorDetail.Gender)
	}
	if p.AuthorDetail.Introduce != "" {
		query += `,d.introduce=?`
		args = append(args, p.AuthorDetail.Introduce)
	}
	if p.AuthorDetail.Email != "" {
		query += `,d.email=?`
		args = append(args, p.AuthorDetail.Email)
	}
	if p.AuthorDetail.Address != "" {
		query += `,d.address=?`
		args = append(args, p.AuthorDetail.Address)
	}
	sqlStr := query + ` WHERE a.author_detail=d.detail_id AND a.author_id=?`
	args = append(args, p.AuthorId)
	if _, err = r.db.Exec(sqlStr, args...); err != nil {
		zaplog.L().Error("update author failed", zaplog.Fields(c, p, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// RetrieveAuthor  根据 id 检索作者
func (r *repo) RetrieveAuthor(c *gin.Context) (p *models.ResponseRetrieveAuthor, err error) {
	authorId := c.Param("authorId")
	p = new(models.ResponseRetrieveAuthor)
	sqlStr := `SELECT a.author_id, a.author_name, d.gender, d.introduce, d.email, d.address,
			  d.create_time, d.last_update_time, a1.admin_name AS create_by, a2.admin_name AS last_update_by 
			  FROM app__author a, app__author_detail d, manage__admin a1, manage__admin a2 
			  WHERE a.author_detail=d.detail_id
			  AND d.create_by=a1.admin_id
			  AND d.last_update_by=a2.admin_id
			  AND a.author_id=?`
	if err = r.db.Get(p, sqlStr, authorId); err != nil {
		if err == sql.ErrNoRows {
			zaplog.L().Warn("no author was selected by id", zaplog.Fields(c, authorId, nil)...)
			resp.UnprovedParam(c, errAuthorNotExists.Error())
			return
		}
		zaplog.L().Error("query author by id failed", zaplog.Fields(c, authorId, err)...)
		resp.ServerBusy(c)
		return
	}
	return
}

// DeleteAuthor 根据 id 删除作者
func (r *repo) DeleteAuthor(c *gin.Context) (err error) {
	authorId := c.Param("authorId")
	sqlStr := `DELETE a, d FROM app__author a, app__author_detail d
			WHERE a.author_detail=d.detail_id AND a.author_id=?`
	var result sql.Result
	if result, err = r.db.Exec(sqlStr, authorId); err != nil {
		zaplog.L().Error("delete author by id failed", zaplog.Fields(c, authorId, err)...)
		resp.ServerBusy(c)
		return
	}
	if effectedRows, _ := result.RowsAffected(); effectedRows == 0 {
		err = errAuthorNotExists
		zaplog.L().Warn("no author was deleted by id", zaplog.Fields(c, authorId, nil)...)
		resp.UnprovedParam(c, errAuthorNotExists.Error())
		return
	}
	return
}

// ListAuthor 展示作者列表
func (r *repo) ListAuthor(c *gin.Context, p *models.RequestListAuthor) (data *models.ResponseListAuthor, err error) {
	data = new(models.ResponseListAuthor)
	records := make([]*models.AAuthor, 0, p.PageSize.Size)
	query := `SELECT a.author_id, a.author_name, d.gender, d.create_time 
			FROM app__author a, app__author_detail d, manage__admin ad 
			WHERE a.author_detail=d.detail_id
            AND d.create_by=ad.admin_id`
	var args []interface{}
	if p.AuthorId != "" {
		query += ` AND a.author_id=?`
		args = append(args, p.AuthorId)
	}
	if p.AuthorName != "" {
		query += ` AND a.author_name=?`
		args = append(args, p.AuthorName)
	}
	if p.Gender != "" {
		query += ` AND d.gender=?`
		args = append(args, p.Gender)
	}
	if p.CreateBy != "" {
		query += ` AND ad.admin_name=?`
		args = append(args, p.CreateBy)
	}
	if p.TimeRange.TimeRangeStart != "" && p.TimeRange.TimeRangeEnd != "" {
		query += ` AND d.create_time BETWEEN ? AND ?`
		args = append(args, p.TimeRange.TimeRangeStart)
		args = append(args, p.TimeRange.TimeRangeEnd)
	}
	sqlStr := query + ` LIMIT ? OFFSET ?`
	args = append(args, p.PageSize.Size)
	args = append(args, (p.PageSize.Page-1)*p.PageSize.Size)
	if err = r.db.Select(&records, sqlStr, args...); err != nil {
		zaplog.L().Error("query author failed", zaplog.Fields(c, args, err)...)
		resp.ServerBusy(c)
		return
	}
	var count int64
	sqlStr = fmt.Sprintf(`SELECT COUNT(*) FROM (%s) AS alias`, query)
	if err = r.db.Get(&count, sqlStr, args[:len(args)-2]...); err != nil {
		zaplog.L().Error("query author count failed", zaplog.Fields(c, args, err)...)
		resp.ServerBusy(c)
		return
	}
	data.Count = count
	data.Records = records
	return
}
